package cn.seaboot.admin.mvc.argument;

import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.commons.exception.BizException;
import cn.seaboot.commons.lang.Null;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;

/**
 * 参数校验
 * <p>
 * 请求参数校验工具，主要用于补充 @RequestParam 的功能，增加 NotEmpty 校验
 * <p>
 * 同类注解：{@link javax.validation.constraints.NotEmpty}
 *
 * @author Mr.css on 2018-05-11.
 */
public class ArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * {@link org.springframework.boot.autoconfigure.web.format.WebConversionService}
     */
    private final ConversionService conversionService;

    /**
     * ConversionService is necessary, you can get ConversionService instance from spring-context
     *
     * @param conversionService form spring-context
     */
    public ArgumentResolver(@NotNull ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Params requestParam = parameter.getParameterAnnotation(Params.class);
        return requestParam != null;
    }

    /**
     * Convenience operation for converting a source object to the specified targetType,
     * where the target type is a descriptor that provides additional conversion context.
     * Simply delegates to {@link ConversionService #convert(Object, TypeDescriptor, TypeDescriptor)} and
     * encapsulates the construction of the source type descriptor using
     * {@link TypeDescriptor#forObject(Object)}.
     *
     * @return the converted value
     * @throws ConversionException      if a conversion exception occurred
     * @throws IllegalArgumentException if targetType is {@code null},
     *                                  or sourceType is {@code null} but source is not {@code null}
     */
    @Override
    @SuppressWarnings("all")
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);

        Params params = methodParameter.getParameterAnnotation(Params.class);
        // get parameter name
        String paramName = params.value();
        if (Null.STRING.equals(paramName)) {
            paramName = methodParameter.getParameterName();
        }

        // get value by parameter name
        String clientParam = request.getParameter(paramName);
        if (clientParam == null) {
            clientParam = params.defaultValue();
        }

        if (params.notEmpty()) {
            if (CommonUtils.isEmpty(clientParam)) {
                // if value is empty, throw a exception
                throw new BizException(CommonUtils.nvl(params.label(), paramName) + " 不能为空!");
            } else if (clientParam.length() > params.length()) {
                // if value's length out max range, throw a exception
                throw new BizException(CommonUtils.nvl(params.label(), paramName) + " 超出最大长度限制：" + params.length() + "!");
            }
        }
        if (methodParameter.getParameterType() == String.class) {
            return clientParam;
        } else {
            return conversionService.convert(clientParam, TypeDescriptor.forObject(clientParam), new TypeDescriptor(methodParameter));
        }
    }
}
